/*
 * Copyright 2006 Matt Jensen
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.spectaql;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Collection;

/**
 * <p>
 * <code>QueryBuilderSupport</code> is a helper implementation of the {@link QueryCallback} interface. It hides
 * interactions with the query context behind its own {@link QueryContext} implementation and handles construction of
 * the opaque query object internally.
 * </p>
 * <p>
 * <strong>Thread Safety:</strong> Instances of this thread are not thread safe and must be created and accessed from a
 * single thread. 
 * </p>
 */
public abstract class QueryCallbackSupport implements QueryCallback, QueryContext
{
    private static Log LOG = LogFactory.getLog(QueryCallbackSupport.class);

    /**
     * <p>
     * The query context. 
     * </p>
     */
    private QueryContext m_context;

    /**
     * <p>
     * Construct a {@link QueryCallbackSupport} instance.
     * </p>
     */
    public QueryCallbackSupport()
    {
        super();
    }

    /* inherit javadoc */
    public final Query buildQuery(final QueryContext context)
    {
        if (null == context)
        {
            throw new NullPointerException("The [context] argument cannot be null.");
        }
        if (null != m_context)
        {
            throw new IllegalStateException(String.format("Query callback [%s] cannot be reused.", this));
        }

        // Store the context and invoke the implementation class to build the query.
        m_context = context;
        final Query result = buildQuery();
        if (LOG.isDebugEnabled())
        {
            LOG.debug(String.format("Returning query [%s] from callback [%s].", result, this));
        }
        return result;
    }

    /* inherit javadoc */
    public <T> T matcher(final Class<T> clazz) throws IllegalArgumentException
    {
        return m_context.matcher(clazz);
    }

    /* inherit javadoc */
    public <T> ConstraintSetters<T> constrain(final T propertyReference)
    {
        return m_context.constrain(propertyReference);
    }

    /* inherit javadoc */
    public <T> CollectionConstraintSetters<T> constrain(final Collection<T> propertyReference)
    {
        return m_context.constrain(propertyReference);
    }

    /**
     * <p>
     * Build the query. Subclasses should implement this method by calling whatever {@link QueryContext} methods (on
     * this instance) are necessary to build and return a query.
     * </p>
     *
     * @return {@link Query} instance encapsulating the dynamic query.
     */
    protected abstract Query buildQuery();
}
